﻿// Copyright (C) 2009 - 2013 EvTech Project
//
// This program is free software: you can redistribute it and/or modify
// it under the +terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;

namespace Piirto
{
    abstract class DrawTool
    {
        protected Color color;
        protected int penWidth;

        public DrawTool(Color color, int penWidth)
        {
            this.color = color;
            this.penWidth = penWidth;
        }


        public int getPenWidth()
        {
            return penWidth;
        }

        public void setPenWidth(int width)
        {
            penWidth = width;
        }

        public void setColor(Color color)
        {
            this.color = color;
        }

        public abstract bool newPoint(int x, int y);

        public abstract void Paint(Graphics g, int dx, int dy, float zoom, float angle);

        public abstract bool contains(int x, int y, int dx, int dy, float zoom, float angle);

        public abstract Rectangle getRectangle();

    }

    class FreehandTool : DrawTool
    {
        private List<Point> points;
        protected Rectangle rec;

        public FreehandTool(Color color, int penWidth)
            : base(color, penWidth)
        {
            points = new List<Point>();
        }

        public override bool newPoint(int x, int y)
        {
            if(rec.IsEmpty)
            {
                rec = new Rectangle(x, y, 0, 0);
            }
            if(x<rec.X)
            {
                rec.Width = rec.Width + rec.X - x;
                rec.X = x;
            }
            else if(x>rec.X+rec.Width)
            {
                rec.Width = x - rec.X;
            }
            if(y<rec.Y)
            {
                rec.Height = rec.Height + rec.Y - y;
                rec.Y = y;
            }
            else if(y>rec.Y+rec.Height)
            {
                rec.Height = y - rec.Y;
            }
            points.Add(new Point(x, y));
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (points.Count < 1)
            {
                return;
            }
            if (zoom == 0)
                zoom = 0.1f;
            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            Matrix origMatrix = g.Transform;

            Matrix matrix = new Matrix();

            matrix.Translate(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2);
            matrix.Scale(zoom, zoom);
            matrix.Translate(-(getRectangle().X + getRectangle().Width / 2), -(getRectangle().Y + getRectangle().Height / 2));

            matrix.Translate(dx / zoom, dy / zoom);
            matrix.RotateAt(angle, new Point(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2));
        

            g.Transform = matrix;
            Pen pen = new Pen(color, penWidth);
            for (int i = 0; i < points.Count; i++)
            {
                g.FillEllipse(new SolidBrush(color), points[i].X - penWidth / 2,
                    points[i].Y - penWidth / 2, penWidth, penWidth);
            }
            for (int i = 1; i < points.Count; i++)
            {
                g.DrawLine(pen, points[i - 1].X, points[i - 1].Y, points[i].X, points[i].Y);
            }
            g.Transform = origMatrix;

        }

        internal List<Point> getPoints()
        {
            return points;
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            bool retVal = false;
            int w = (int)(getRectangle().Width * zoom);
            int h = (int)(getRectangle().Height * zoom);
            Rectangle newRec = new Rectangle((int)(getRectangle().X + dx - (w - getRectangle().Width) / 2),
                (int)(getRectangle().Y + dy - (h - getRectangle().Height) / 2), w, h);
            Region region = new Region(newRec);
            Matrix matrix = new Matrix();
            matrix.RotateAt(angle, new Point(newRec.X + newRec.Width / 2, newRec.Y + newRec.Height / 2));
            region.Transform(matrix);
            if (region.IsVisible(x, y))
            {
                Bitmap bm = new Bitmap(rec.Width, rec.Height);
                Graphics g = Graphics.FromImage(bm);
                Pen pen = new Pen(color, penWidth);
                for (int i = 0; i < points.Count; i++)
                {
                    g.FillEllipse(new SolidBrush(color), points[i].X - penWidth / 2 - rec.X,
                        points[i].Y - penWidth / 2 - rec.Y, penWidth, penWidth);
                }
                for (int i = 1; i < points.Count; i++)
                {
                    g.DrawLine(pen, points[i - 1].X - rec.X, points[i - 1].Y - rec.Y,
                        points[i].X - rec.X, points[i].Y - rec.Y);
                }
                g.Dispose();
                int cx = newRec.X + newRec.Width / 2;
                int cy = newRec.Y + newRec.Height / 2;
                double hyp = Math.Sqrt(Math.Pow(cx - x, 2) + Math.Pow(cy - y, 2));
                double tmpAngle = Math.Atan2((x - cx),
                    y - cy) * -(180 / Math.PI) - angle;
                while (tmpAngle < 0)
                {
                    tmpAngle += 360;
                }
                int xForCentre = (int)(Math.Sin(tmpAngle / -(180 / Math.PI)) * hyp);
                int yForCentre = (int)(Math.Sqrt((Math.Pow(hyp,2) - Math.Pow(xForCentre,2))));

                int imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                int imgY = 0;
                imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                if (tmpAngle > 270 || tmpAngle < 90)
                {
                    imgY = (newRec.Height / 2 + yForCentre) * bm.Height / newRec.Height;
                }
                else
                {
                    imgY = (newRec.Height / 2 - yForCentre) * bm.Height / newRec.Height;
                }
                if (bm.GetPixel(imgX, imgY).A > 50)
                {
                    retVal = true;
                }
            }

            return retVal;
        }

        public override Rectangle getRectangle()
        {
            return rec;
        }
    }

    class LineTool : DrawTool
    {
        private Point startPoint;
        private Point endPoint;

        public LineTool(Color color, int penWidth)
            : base(color, penWidth)
        {
        }

        public override bool newPoint(int x, int y)
        {
            if (startPoint.IsEmpty)
            {
                startPoint = new Point(x, y);
            }
            endPoint = new Point(x, y);
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            Matrix origMatrix = g.Transform;

            Matrix matrix = new Matrix();

            matrix.Translate(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2);
            matrix.Scale(zoom, zoom);
            matrix.Translate(-(getRectangle().X + getRectangle().Width / 2), -(getRectangle().Y + getRectangle().Height / 2));

            matrix.Translate(dx / zoom, dy / zoom);
            matrix.RotateAt(angle, new Point(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2));


            g.Transform = matrix;
            Pen pen = new Pen(color, penWidth);
            g.DrawLine(pen, startPoint, endPoint);
            g.Transform = origMatrix;
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            bool retVal = false;
            int w = (int)(getRectangle().Width * zoom);
            int h = (int)(getRectangle().Height * zoom);
            Rectangle newRec = new Rectangle((int)(getRectangle().X + dx - (w - getRectangle().Width) / 2),
                (int)(getRectangle().Y + dy - (h - getRectangle().Height) / 2), w, h);
            Region region = new Region(newRec);
            Matrix matrix = new Matrix();
            matrix.RotateAt(angle, new Point(newRec.X + newRec.Width / 2, newRec.Y + newRec.Height / 2));
            region.Transform(matrix);
            if (region.IsVisible(x, y))
            {
                Bitmap bm = new Bitmap(getRectangle().Width, getRectangle().Height);
                Graphics g = Graphics.FromImage(bm);
                Pen pen = new Pen(color, penWidth);
                g.DrawLine(pen, startPoint.X - getRectangle().X, startPoint.Y - getRectangle().Y,
                    endPoint.X - getRectangle().X, endPoint.Y - getRectangle().Y);
                g.Dispose();
                int cx = newRec.X + newRec.Width / 2;
                int cy = newRec.Y + newRec.Height / 2;
                double hyp = Math.Sqrt(Math.Pow(cx - x, 2) + Math.Pow(cy - y, 2));
                double tmpAngle = Math.Atan2((x - cx),
                    y - cy) * -(180 / Math.PI) - angle;
                while (tmpAngle < 0)
                {
                    tmpAngle += 360;
                }
                int xForCentre = (int)(Math.Sin(tmpAngle / -(180 / Math.PI)) * hyp);
                int yForCentre = (int)(Math.Sqrt((Math.Pow(hyp, 2) - Math.Pow(xForCentre, 2))));

                int imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                int imgY = 0;
                imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                if (tmpAngle > 270 || tmpAngle < 90)
                {
                    imgY = (newRec.Height / 2 + yForCentre) * bm.Height / newRec.Height;
                }
                else
                {
                    imgY = (newRec.Height / 2 - yForCentre) * bm.Height / newRec.Height;
                }
                if (bm.GetPixel(imgX, imgY).A > 50)
                {
                    retVal = true;
                }
            }

            return retVal;
        }

        public override Rectangle getRectangle()
        {
            Rectangle rec;
            if (!startPoint.IsEmpty && !endPoint.IsEmpty)
            {
                rec = new Rectangle(Math.Min(startPoint.X, endPoint.X),
                    Math.Min(startPoint.Y, endPoint.Y),
                    Math.Abs(startPoint.X - endPoint.X),
                    Math.Abs(startPoint.Y - endPoint.Y));
            }
            else
            {
                rec = new Rectangle();
            }
            return rec;
        }
    }

    class CircleTool : DrawTool
    {
        private Point startPoint;
        private Point endPoint;

        public CircleTool(Color color, int penWidth)
            : base(color, penWidth)
        {
        }

        public override bool newPoint(int x, int y)
        {
            if (startPoint.IsEmpty)
            {
                startPoint = new Point(x, y);
            }
            endPoint = new Point(x, y);
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (!startPoint.IsEmpty && !endPoint.IsEmpty)
            {
                if (zoom == 0)
                    zoom = 0.1f;

                g.InterpolationMode = InterpolationMode.NearestNeighbor;
                Matrix origMatrix = g.Transform;

                Matrix matrix = new Matrix();

                matrix.Translate(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2);
                matrix.Scale(zoom, zoom);
                matrix.Translate(-(getRectangle().X + getRectangle().Width / 2), -(getRectangle().Y + getRectangle().Height / 2));

                matrix.Translate(dx / zoom, dy / zoom);
                matrix.RotateAt(angle, new Point(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2));


                g.Transform = matrix;
                Pen pen = new Pen(color, penWidth);
                Rectangle rec = new Rectangle(Math.Min(startPoint.X, endPoint.X),
                    Math.Min(startPoint.Y, endPoint.Y),
                    Math.Abs(startPoint.X - endPoint.X),
                    Math.Abs(startPoint.Y - endPoint.Y));
                g.DrawEllipse(pen, rec);
                g.Transform = origMatrix;
            }
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            bool retVal = false;
            int w = (int)(getRectangle().Width * zoom);
            int h = (int)(getRectangle().Height * zoom);
            Rectangle newRec = new Rectangle((int)(getRectangle().X + dx - (w - getRectangle().Width) / 2),
                (int)(getRectangle().Y + dy - (h - getRectangle().Height) / 2), w, h);
            Region region = new Region(newRec);
            Matrix matrix = new Matrix();
            matrix.RotateAt(angle, new Point(newRec.X + newRec.Width / 2, newRec.Y + newRec.Height / 2));
            region.Transform(matrix);
            if (region.IsVisible(x, y))
            {
                Bitmap bm = new Bitmap(getRectangle().Width, getRectangle().Height);
                Graphics g = Graphics.FromImage(bm);
                Pen pen = new Pen(color, penWidth);
                Rectangle rec = new Rectangle(Math.Min(startPoint.X, endPoint.X) - getRectangle().X,
                    Math.Min(startPoint.Y, endPoint.Y) - getRectangle().Y,
                    Math.Abs(startPoint.X - endPoint.X),
                    Math.Abs(startPoint.Y - endPoint.Y));
                g.DrawEllipse(pen, rec);
                g.Dispose();
                int cx = newRec.X + newRec.Width / 2;
                int cy = newRec.Y + newRec.Height / 2;
                double hyp = Math.Sqrt(Math.Pow(cx - x, 2) + Math.Pow(cy - y, 2));
                double tmpAngle = Math.Atan2((x - cx),
                    y - cy) * -(180 / Math.PI) - angle;
                while (tmpAngle < 0)
                {
                    tmpAngle += 360;
                }
                int xForCentre = (int)(Math.Sin(tmpAngle / -(180 / Math.PI)) * hyp);
                int yForCentre = (int)(Math.Sqrt((Math.Pow(hyp, 2) - Math.Pow(xForCentre, 2))));

                int imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                int imgY = 0;
                imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                if (tmpAngle > 270 || tmpAngle < 90)
                {
                    imgY = (newRec.Height / 2 + yForCentre) * bm.Height / newRec.Height;
                }
                else
                {
                    imgY = (newRec.Height / 2 - yForCentre) * bm.Height / newRec.Height;
                }
                if (bm.GetPixel(imgX, imgY).A > 50)
                {
                    retVal = true;
                }
            }

            return retVal;
        }

        public override Rectangle getRectangle()
        {
            Rectangle rec;
            if (!startPoint.IsEmpty && !endPoint.IsEmpty)
            {
                rec = new Rectangle(Math.Min(startPoint.X, endPoint.X),
                    Math.Min(startPoint.Y, endPoint.Y),
                    Math.Abs(startPoint.X - endPoint.X),
                    Math.Abs(startPoint.Y - endPoint.Y));
            }
            else
            {
                rec = new Rectangle();
            }
            return rec;
        }
    }

    class RectangleTool : DrawTool
    {
        private Point startPoint;
        private Point endPoint;

        public RectangleTool(Color color, int penWidth)
            : base(color, penWidth)
        {
        }

        public override bool newPoint(int x, int y)
        {
            if (startPoint.IsEmpty)
            {
                startPoint = new Point(x, y);
            }
            endPoint = new Point(x, y);
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (!startPoint.IsEmpty && !endPoint.IsEmpty)
            {
                if (zoom == 0)
                    zoom = 0.1f;

                g.InterpolationMode = InterpolationMode.NearestNeighbor;
                Matrix origMatrix = g.Transform;

                Matrix matrix = new Matrix();

                matrix.Translate(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2);
                matrix.Scale(zoom, zoom);
                matrix.Translate(-(getRectangle().X + getRectangle().Width / 2), -(getRectangle().Y + getRectangle().Height / 2));

                matrix.Translate(dx / zoom, dy / zoom);
                matrix.RotateAt(angle, new Point(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2));


                g.Transform = matrix;
                Pen pen = new Pen(color, penWidth);
                Rectangle rec = new Rectangle(Math.Min(startPoint.X, endPoint.X),
                    Math.Min(startPoint.Y, endPoint.Y),
                    Math.Abs(startPoint.X - endPoint.X),
                    Math.Abs(startPoint.Y - endPoint.Y));
                g.DrawRectangle(pen, rec);
                g.Transform = origMatrix;
            }
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            bool retVal = false;
            int w = (int)(getRectangle().Width * zoom);
            int h = (int)(getRectangle().Height * zoom);
            Rectangle newRec = new Rectangle((int)(getRectangle().X + dx - (w - getRectangle().Width) / 2),
                (int)(getRectangle().Y + dy - (h - getRectangle().Height) / 2), w, h);
            Region region = new Region(newRec);
            Matrix matrix = new Matrix();
            matrix.RotateAt(angle, new Point(newRec.X + newRec.Width / 2, newRec.Y + newRec.Height / 2));
            region.Transform(matrix);
            if (region.IsVisible(x, y))
            {
                Bitmap bm = new Bitmap(getRectangle().Width, getRectangle().Height);
                Graphics g = Graphics.FromImage(bm);
                Pen pen = new Pen(color, penWidth);
                Rectangle rec = new Rectangle(Math.Min(startPoint.X, endPoint.X) - getRectangle().X,
                    Math.Min(startPoint.Y, endPoint.Y) - getRectangle().Y,
                    Math.Abs(startPoint.X - endPoint.X),
                    Math.Abs(startPoint.Y - endPoint.Y));
                g.DrawRectangle(pen, rec);
                g.Dispose();
                int cx = newRec.X + newRec.Width / 2;
                int cy = newRec.Y + newRec.Height / 2;
                double hyp = Math.Sqrt(Math.Pow(cx - x, 2) + Math.Pow(cy - y, 2));
                double tmpAngle = Math.Atan2((x - cx),
                    y - cy) * -(180 / Math.PI) - angle;
                while (tmpAngle < 0)
                {
                    tmpAngle += 360;
                }
                int xForCentre = (int)(Math.Sin(tmpAngle / -(180 / Math.PI)) * hyp);
                int yForCentre = (int)(Math.Sqrt((Math.Pow(hyp, 2) - Math.Pow(xForCentre, 2))));

                int imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                int imgY = 0;
                imgX = (newRec.Width / 2 + xForCentre) * bm.Width / newRec.Width;
                if (tmpAngle > 270 || tmpAngle < 90)
                {
                    imgY = (newRec.Height / 2 + yForCentre) * bm.Height / newRec.Height;
                }
                else
                {
                    imgY = (newRec.Height / 2 - yForCentre) * bm.Height / newRec.Height;
                }
                if (bm.GetPixel(imgX, imgY).A > 50)
                {
                    retVal = true;
                }
            }

            return retVal;
        }

        public override Rectangle getRectangle()
        {
            Rectangle rec;
            if (!startPoint.IsEmpty && !endPoint.IsEmpty)
            {
                rec = new Rectangle(Math.Min(startPoint.X, endPoint.X),
                    Math.Min(startPoint.Y, endPoint.Y),
                    Math.Abs(startPoint.X - endPoint.X),
                    Math.Abs(startPoint.Y - endPoint.Y));
            }
            else
            {
                rec = new Rectangle();
            }
            return rec;
        }
    }

    class FillTool : DrawTool
    {
        private Bitmap fillImg;
        int x, y;
        public const uint FLOODFILLBORDER = 0;
        public const uint FLOODFILLSURFACE = 1;
        public static Color dontColor = Color.Black;

        public FillTool(Color color, Image image)
            :base(color, 5)
        {
            fillImg = new Bitmap(image);
        }

        [DllImport("gdi32.dll")]
        static extern bool FloodFill(IntPtr hdc, int nXStart, int nYStart,
           uint crFill);
        [DllImport("gdi32.dll")]
        public static extern bool ExtFloodFill(IntPtr hdc, int nXStart, int nYStart, uint crColor, uint fuFillType);
        [DllImport("gdi32.dll")]
        static extern IntPtr CreateSolidBrush(uint crColor);
        [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
        public static extern uint DeleteObject(IntPtr hdc);
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
        [DllImport ("gdi32.dll",EntryPoint="CreateCompatibleDC")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
        [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
        public static extern IntPtr DeleteDC(IntPtr hDc);
        [DllImport("gdi32.dll", EntryPoint = "GetPixel")]
        public static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);
        [DllImport("gdi32.dll", EntryPoint = "SetPixel")]
        static extern uint SetPixel(IntPtr hdc, int X, int Y, uint crColor);
        [DllImport("gdi32.dll", EntryPoint = "GdiAlphaBlend")]
        public static extern bool AlphaBlend(IntPtr hdcDest, int nXOriginDest, int nYOriginDest,
            int nWidthDest, int nHeightDest,
            IntPtr hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
            BLENDFUNCTION blendFunction);

        [StructLayout(LayoutKind.Sequential)]
        public struct BLENDFUNCTION
        {
            byte BlendOp;
            byte BlendFlags;
            byte SourceConstantAlpha;
            byte AlphaFormat;

            public BLENDFUNCTION(byte op, byte flags, byte alpha, byte format)
            {
                BlendOp = op;
                BlendFlags = flags;
                SourceConstantAlpha = alpha;
                AlphaFormat = format;
            }
        }
        const byte AC_SRC_OVER = 0x00;
        const byte AC_SRC_ALPHA = 0x01;
        
        public static uint RGB(Color color)
        {
            byte byRed, byGreen, byBlue, RESERVED;

            byRed = color.R;
            byGreen = color.G;
            byBlue = color.B;
            RESERVED = 0;
            byte[] RGBCOLORS = new byte[4];
            RGBCOLORS[0] = byRed;
            RGBCOLORS[1] = byGreen;
            RGBCOLORS[2] = byBlue;
            RGBCOLORS[3] = RESERVED;
            return BitConverter.ToUInt32(RGBCOLORS, 0);
        } 

        public override bool newPoint(int x, int y)
        {
            if (dontColor != null && fillImg.GetPixel(x, y).ToArgb() == dontColor.ToArgb())
            {
                return false;
            }
            this.x = x;
            this.y = y;
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (dontColor != null && fillImg.GetPixel(x, y).ToArgb() == dontColor.ToArgb())
            {
                return;
            }
            IntPtr hdc = g.GetHdc();

            IntPtr overlayImageCDC = CreateCompatibleDC(hdc);
            IntPtr overlayImageHandle;
            try
            {
                overlayImageHandle = fillImg.GetHbitmap();
            }
            catch (Exception)
            {
                g.ReleaseHdc(hdc);
                Console.Out.WriteLine("get an overlayImageHandle failed");
                return;
            }
            SelectObject(overlayImageCDC, overlayImageHandle);

            AlphaBlend(hdc, 0, 0, fillImg.Width, fillImg.Height,
                overlayImageCDC, 0, 0, fillImg.Width, fillImg.Height,
                new BLENDFUNCTION(AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA));
            DeleteObject(overlayImageHandle);
            DeleteDC(overlayImageCDC);

            
            IntPtr gdiBrsh = CreateSolidBrush(RGB(color));
            SelectObject(hdc, gdiBrsh);

            ExtFloodFill(hdc, x, y, GetPixel(hdc, x, y), FLOODFILLSURFACE); 
            DeleteObject(gdiBrsh);
            g.ReleaseHdc(hdc);
        }


        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            return false;
        }

        internal void reFill(Image image)
        {

            fillImg = new Bitmap(image);
        }

        public override Rectangle getRectangle()
        {
            return new Rectangle();
        }
    }

    class EraserTool : DrawTool
    {
        private List<Point> points;
        protected Rectangle rec;

        public EraserTool(Color color, int penWidth)
            : base(color, penWidth)
        {
            points = new List<Point>();
        }

        public override bool newPoint(int x, int y)
        {
            if (rec == null)
            {
                rec = new Rectangle(x, y, 0, 0);
            }
            if (x < rec.X)
            {
                rec.Width = rec.Width + rec.X - x;
                rec.X = x;
            }
            else if (x > rec.X + rec.Width)
            {
                rec.Width = x - rec.X;
            }
            if (y < rec.Y)
            {
                rec.Height = rec.Height + rec.Y - y;
                rec.Y = y;
            }
            else if (y > rec.Y + rec.Height)
            {
                rec.Width = y - rec.Y;
            }
            points.Add(new Point(x, y));
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (points.Count < 1)
            {
                return;
            }
            Pen pen = new Pen(color, penWidth);
            for (int i = 0; i < points.Count; i++)
            {
                g.FillEllipse(new SolidBrush(color), points[i].X - penWidth / 2,
                    points[i].Y - penWidth / 2, penWidth, penWidth);
            }
            for (int i = 1; i < points.Count; i++)
            {
                g.DrawLine(pen, points[i - 1].X, points[i - 1].Y, points[i].X, points[i].Y);
            }
        }

        internal List<Point> getPoints()
        {
            return points;
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            return false;
        }

        public override Rectangle getRectangle()
        {
            return rec;
        }
    }

    class StampTool : DrawTool
    {
        private Bitmap stampImg;
        private Point locationStart;
        private Rectangle stampRect;

        public StampTool(Color color, Image image)
            : base(color, 5)
        {
            stampImg = new Bitmap(image);
            stampRect = new Rectangle();
        }

        public override bool newPoint(int x, int y)
        {
            if (stampImg == null || stampImg.Width == 0 || stampImg.Height == 0)
                return false;

            if (locationStart.IsEmpty)
            {
                locationStart = new Point(x, y);
                stampRect.X = locationStart.X;
                stampRect.Y = locationStart.Y;
                stampRect.Width = penWidth;
                stampRect.Height = penWidth;

            }
            else
            {
                if (locationStart.X < x)
                {
                    stampRect.X = locationStart.X;
                    stampRect.Width = x - locationStart.X;
                }
                else
                {
                    stampRect.X = x;
                    stampRect.Width = locationStart.X - x;
                }
                if (locationStart.Y < y)
                {
                    stampRect.Y = locationStart.Y;
                    stampRect.Height = y - locationStart.Y;
                }
                else
                {
                    stampRect.Y = y;
                    stampRect.Height = locationStart.Y - y;
                }
                stampRect.Width = stampRect.Width * 2;
                stampRect.Height = stampRect.Height*2;
                if (stampRect.Height < penWidth)
                {
                    stampRect.Height = penWidth;
                }
                if (stampRect.Width < penWidth)
                {
                    stampRect.Width = penWidth;
                }
            }
            if (stampRect.Width * stampImg.Height / stampImg.Width > stampRect.Height)
            {
                stampRect.Height = stampRect.Width * stampImg.Height / stampImg.Width;
                if (locationStart.Y > y)
                {
                    stampRect.Y = locationStart.Y - stampRect.Height;
                }
            }
            else
            {
                stampRect.Width = stampRect.Height * stampImg.Width / stampImg.Height;
                if (locationStart.X > x)
                {
                    stampRect.X = locationStart.X - stampRect.Width;
                }
            }
            stampRect.X = stampRect.X - stampRect.Width/2;
            stampRect.Y = stampRect.Y - stampRect.Height/2;
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            g.InterpolationMode = InterpolationMode.NearestNeighbor;
            Matrix origMatrix = g.Transform;

            Region region = new Region(getRectangle());
            Matrix matrix = new Matrix();

            matrix.Translate(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2);
            matrix.Scale(zoom, zoom);
            matrix.Translate(-(getRectangle().X + getRectangle().Width / 2), -(getRectangle().Y + getRectangle().Height / 2));

            matrix.Translate(dx / zoom, dy / zoom);
            matrix.RotateAt(angle, new Point(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2));
            region.Transform(matrix);

            g.Transform = matrix;
            g.DrawImage(stampImg, getRectangle().X, getRectangle().Y, getRectangle().Width, getRectangle().Height);
            g.Transform = origMatrix;

        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            bool retVal = false;
            int w = (int)(stampRect.Width * zoom);
            int h = (int)(stampRect.Height * zoom);
            Rectangle newRec = new Rectangle((int)(stampRect.X + dx - (w - stampRect.Width) / 2), 
                (int)(stampRect.Y + dy - (h - stampRect.Height) / 2), w, h);
            Region region = new Region(newRec);
            Matrix matrix = new Matrix();
            matrix.RotateAt(angle, new Point(newRec.X + newRec.Width / 2, newRec.Y + newRec.Height / 2));
            region.Transform(matrix);
            if (region.IsVisible(x, y))
            {
                int cx = newRec.X + newRec.Width / 2;
                int cy = newRec.Y + newRec.Height / 2;
                double hyp = Math.Sqrt(Math.Pow(cx - x, 2) + Math.Pow(cy - y, 2));
                double tmpAngle = Math.Atan2((x - cx),
                    y - cy) * -(180 / Math.PI) - angle;
                while (tmpAngle < 0)
                {
                    tmpAngle += 360;
                }
                int xForCentre = (int)(Math.Sin(tmpAngle / -(180 / Math.PI)) * hyp);
                int yForCentre = (int)(Math.Sqrt((Math.Pow(hyp,2) - Math.Pow(xForCentre,2))));

                int imgX = (newRec.Width / 2 + xForCentre) * stampImg.Width / newRec.Width;
                int imgY = 0;
                imgX = (newRec.Width / 2 + xForCentre) * stampImg.Width / newRec.Width;
                if (tmpAngle > 270 || tmpAngle < 90)
                {
                    imgY = (newRec.Height / 2 + yForCentre) * stampImg.Height / newRec.Height;
                }
                else
                {
                    imgY = (newRec.Height / 2 - yForCentre) * stampImg.Height / newRec.Height;
                }
                if (stampImg.GetPixel(imgX, imgY).A > 50)
                {
                    retVal = true;
                }
                //retVal = true;
            }
            
            return retVal;
        }

        public override Rectangle getRectangle()
        {
            return stampRect;
        }
    }

    class MoveTool : DrawTool
    {
        private DrawTool tool;
        private Point firstLoc, currentLoc;
        private int origIndex;
        private Rectangle oldRec;

        public MoveTool(DrawTool tool, int origIndex)
            : base(Color.White, 5)
        {
            this.tool = tool;
            this.origIndex = origIndex;
            oldRec = tool.getRectangle();
        }

        public override bool newPoint(int x, int y)
        {
            if (firstLoc.IsEmpty)
            {
                firstLoc = new Point(x, y);
            }
            currentLoc = new Point(x, y);
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (tool == null)
            {
                return;
            }
            else if (firstLoc.IsEmpty || currentLoc.IsEmpty)
            {
                tool.Paint(g, dx, dy, zoom, angle);
            }
            else
            {
                int x = currentLoc.X - firstLoc.X + dx;
                int y = currentLoc.Y - firstLoc.Y + dy;
                tool.Paint(g, x, y, zoom, angle);
            }
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (tool == null)
            {
                return false;
            }
            else if (firstLoc.IsEmpty || currentLoc.IsEmpty)
            {
                return tool.contains(x, y, dx, dy, zoom, angle);
            }
            else
            {
                return tool.contains(x, y, currentLoc.X - firstLoc.X + dx, 
                    currentLoc.Y - firstLoc.Y + dy, zoom, angle);
            }
        }

        public int getOriginalToolIndex()
        {
            return origIndex;
        }

        public DrawTool getOriginalTool()
        {
            return tool;
        }

        public override Rectangle getRectangle()
        {
            return new Rectangle(oldRec.X + currentLoc.X - firstLoc.X,
                oldRec.Y + currentLoc.Y - firstLoc.Y,
                oldRec.Width, oldRec.Height);
        }
    }


    class ResizeTool : DrawTool
    {
        private DrawTool tool;
        private float zoom;
        private int origIndex;
        private float firsthyp = 0;
        private Rectangle oldRec;

        public ResizeTool(DrawTool tool, int origIndex)
            : base(Color.White, 5)
        {
            this.tool = tool;
            this.origIndex = origIndex;
            zoom = 1;
            oldRec = tool.getRectangle();
        }

        public override bool newPoint(int x, int y)
        {
            if (firsthyp == 0)
            {
                firsthyp = (float)Math.Sqrt(Math.Pow(getRectangle().X + getRectangle().Width / 2 - x, 2) +
                 Math.Pow(getRectangle().Y + getRectangle().Height / 2 - y, 2));
            }
            if (firsthyp < 1)
                firsthyp = 1;

            zoom = (float)Math.Sqrt(Math.Pow(getRectangle().X + getRectangle().Width / 2 - x, 2) +
                Math.Pow(getRectangle().Y + getRectangle().Height / 2 - y, 2)) / firsthyp;

            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            float newZoom = zoom * this.zoom;
            if (tool == null)
            {
                return;
            }
            tool.Paint(g, dx, dy, newZoom, angle);
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            float newZoom = zoom * this.zoom;
            if (tool == null)
            {
                return false;
            }
            return tool.contains(x, y, dx, dy, newZoom, angle);
        }

        public int getOriginalToolIndex()
        {
            return origIndex;
        }

        public DrawTool getOriginalTool()
        {
            return tool;
        }

        public override Rectangle getRectangle()
        {
            int width = (int)(oldRec.Width * zoom);
            int height = (int)(oldRec.Height * zoom);
            return new Rectangle(oldRec.X + (oldRec.Width - width) / 2,
                oldRec.Y + (oldRec.Height - height) / 2,
                width, height);
        }
    }

    class RotateTool : DrawTool
    {
        private DrawTool tool;
        private float angle, startangle;
        private int origIndex;
        private Rectangle oldRec;

        public RotateTool(DrawTool tool, int origIndex)
            : base(Color.White, 5)
        {
            this.tool = tool;
            this.origIndex = origIndex;
            angle = 0;
            startangle = 999;
            oldRec = tool.getRectangle();
        }

        public override bool newPoint(int x, int y)
        {
            int centerX, centerY;
            centerX = oldRec.X + oldRec.Width / 2;
            centerY = oldRec.Y + oldRec.Height / 2;
            if (startangle == 999)
            {
                startangle = (int)(Math.Atan2(x - centerX, y - centerY) * -(180 / Math.PI));
            }
            angle = (int)(Math.Atan2(x - centerX, y - centerY) * -(180 / Math.PI)) - startangle;
            return true;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (tool == null)
            {
                return;
            }
            tool.Paint(g, dx, dy, zoom, angle+this.angle);
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (tool == null)
            {
                return false;
            }
            return tool.contains(x, y, dx, dy, zoom, angle + this.angle);
        }

        public int getOriginalToolIndex()
        {
            return origIndex;
        }

        public DrawTool getOriginalTool()
        {
            return tool;
        }

        public override Rectangle getRectangle()
        {
            return oldRec;
        }
    }
    class TextTool : DrawTool
    {
        private int x, y;
        private String text;
        private Boolean isReady;
        SizeF textSize;

        public TextTool(Color color, int penWidth)
            : base(color, penWidth)
        {
            text = "";
            isReady = false;
            this.x = -1;
            this.y = -1;
        }

        public override bool newPoint(int x, int y)
        {
            this.x = x;
            this.y = y;
            return true;
        }

        public String addChar(char c)
        {
            text += c;
            return text;
        }

        public override void Paint(Graphics g, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            Brush brush = new SolidBrush(color);
            int fontSize = (int)(penWidth * 2);
            if(fontSize<1)
                fontSize = 1;
            if (fontSize > 120)
                fontSize = 120;

            Font font = new Font(FontFamily.GenericSansSerif, fontSize);
            String str;
            if(isReady)
                str = text;
            else
                str = text + "|";

            textSize = g.MeasureString(str, font);
            Matrix origMatrix = g.Transform;

            Region region = new Region(getRectangle());
            Matrix matrix = new Matrix();

            matrix.Translate(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2);
            matrix.Scale(zoom, zoom);
            matrix.Translate(-(getRectangle().X + getRectangle().Width / 2), -(getRectangle().Y + getRectangle().Height / 2));
             
            matrix.Translate(dx/zoom, dy/zoom);
            matrix.RotateAt(angle, new Point(getRectangle().X + getRectangle().Width / 2, getRectangle().Y + getRectangle().Height / 2));
            region.Transform(matrix);

            g.Transform = matrix;
            g.DrawString(str, font, brush, x, y);
            g.Transform = origMatrix;
        }

        public override bool contains(int x, int y, int dx, int dy, float zoom, float angle)
        {
            if (zoom == 0)
                zoom = 0.1f;

            bool retVal = false;
            int w = (int)(getRectangle().Width * zoom);
            int h = (int)(getRectangle().Height * zoom);
            Rectangle newRec = new Rectangle((int)(getRectangle().X + dx - (w - getRectangle().Width) / 2),
                (int)(getRectangle().Y + dy - (h - getRectangle().Height) / 2), w, h);
            Region region = new Region(newRec);
            Matrix matrix = new Matrix();
            matrix.RotateAt(angle, new Point(newRec.X + newRec.Width / 2, newRec.Y + newRec.Height / 2));
            region.Transform(matrix);
            if (region.IsVisible(x, y))
            {
                retVal = true;
            }

            return retVal;
        }

        public override Rectangle getRectangle()
        {
            Rectangle rec;
            if (textSize != null)
            {
                rec = new Rectangle(x, y, (int)textSize.Width, (int)textSize.Height);
            }
            else
            {
                rec = new Rectangle();
            }
            return rec;
        }

        internal bool setReady()
        {
            isReady = true;
            return (text.Trim().Length > 0);
        }

        internal void deleteChar()
        {
            if(text.Length > 0)
                text = text.Substring(0, text.Length - 1);
        }

        internal void setText(string text)
        {
            this.text = text;
        }
    }
}
